#include <stdio.h>
#include <ctype.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>


#define SIZE 512
#define ZERO '\0'
#define SEP " "
#define NUM 32
#define _skippath(p) do{\
  p+=(strlen(p)-1);\
  while(*p!='/') p--;\
}while(0)
#define  _skipspace(cmd,pos)do{while(isspace(cmd[pos]))pos++;}while(0)



#define NONE_REDIR 0
#define IN_REDIR 1
#define OUT_REDIR 2
#define APP_REDIR 3

char _cwd[SIZE*2];
char _lastcwd[SIZE*2];
int lastcode =0;
int redir_type=NONE_REDIR;
char *filename=NULL;

void _die()
{
  exit(1);
}


const char* _home()
{
  const char* home=getenv("HOME");
  if(home==NULL)
    return "/";
  return home;
}


const char *get_username()
{
  const char* name=getenv("USER");
  if(name==NULL)
    return "None";
  else
    return name;

}

const char *get_hostname()
{
 const char* host=getenv("HOSTNAME");
 if(host==NULL)
   return "None";
 else
   return host;

}


void remake_lastcwd()
{
  getcwd(_lastcwd,sizeof(_lastcwd));

}

const char* get_cwd()
{
  const char* cwd=getenv("PWD");
 if(cwd==NULL)
   return "None";
 else
   return cwd;
}

void _makecommandlineandprint()
{
  char line[SIZE];
  const char* username=get_username();
  const char* hostname=get_hostname();
  const char* cwd=get_cwd();
  _skippath(cwd);
  snprintf(line,sizeof(line),"[%s@%s %s]: ",username,hostname,strlen(cwd)==1 ? "/" : cwd+1);
  printf("%s",line);
  fflush(stdout);
}

int _getusercommand(char command[],size_t n)
{

  char *s=fgets(command,n,stdin);
  if(s==NULL) return -1;
  command[strlen(command)-1]=ZERO;
  return strlen(command);
}

char* gArgv[NUM];

void _splitcommand(char command[],size_t n)
{
// "ls -a -l -n" -> "ls" "-a" "-l" "-n"
  gArgv[0]=strtok(command,SEP);
  int index=1;
  while((gArgv[index++]=strtok(NULL,SEP)));
}

void _cd()
{
  const char* path=gArgv[1];
  if(path==NULL)
    path=_home();
  else if(strcmp(path,"-")==0)
  {
    char lcwd[SIZE*2];
    strcpy(lcwd,_lastcwd);
    path=(const char*)lcwd;
  }
  getcwd(_lastcwd,sizeof(_lastcwd));
  chdir(path);
//刷新环境变量
  char temp[SIZE*2];
  getcwd(temp,sizeof(temp));
  snprintf(_cwd,sizeof(_cwd),"PWD=%s",temp);
  putenv(_cwd);
}


void _echo()
{
  printf("%d\n",lastcode);
  lastcode=0;
}


  

void _checkredir(char command[])
{
  int pos=0;
  int end=strlen(command);
  while(pos<end)
  {
    if(command[pos]=='>')
    {
      command[pos++]=0;
      if(command[pos]=='>')
      {
        command[pos++]=0;
        redir_type=APP_REDIR;
        _skipspace(command,pos);
      filename=command+pos;
      }
      else
      {
        redir_type=OUT_REDIR;
        _skipspace(command,pos);
      filename=command+pos;
      }
    }
    else if(command[pos]=='<')
    {
      command[pos++]=0;
      redir_type=IN_REDIR;
      _skipspace(command,pos);
      filename=command+pos;
    }
    else 
      pos++;
    
  }
}



int _checkcommand()
{
  int judge=0;
  const char* targetcmd=gArgv[0];
  if(strcmp(targetcmd,"cd")==0)
  {
    judge=1;
    _cd();
  }
  else if(strcmp(targetcmd,"echo")==0 && strcmp(gArgv[1],"$?")==0)
  {
    judge=1;
    _echo();
  }

  return judge;
}


void _executecommand()
{
  pid_t id=fork();
 if (id<0)// 创建失败
   _die();
 else if(id==0)
 {
//子进程
  //重定向设置
  if(filename!=NULL)
  {
    if(redir_type==IN_REDIR)
    {
      int fd=open(filename,O_RDONLY);
      dup2(fd,0);
    }
    else if(redir_type==OUT_REDIR)
    {
      int fd=open(filename,O_WRONLY | O_CREAT | O_TRUNC,0666);
      dup2(fd,1);
    }
    else if(redir_type==APP_REDIR)
    {
      int fd=open(filename,O_WRONLY | O_CREAT | O_APPEND,0666);
      dup2(fd,1);
    }
  }
   execvp(gArgv[0],gArgv);
  exit(errno);
 }
 else
 {
   //父进程
   int status=0;
   pid_t rid=waitpid(id,&status,0);
   if(rid>0)
   {
     lastcode=WEXITSTATUS(status);
     if(lastcode!=0)
       printf("%s:%s:%d\n",gArgv[0],strerror(lastcode),lastcode);
   }
 }
}

int main()
{
  int quit=0;
    remake_lastcwd();
  while(!quit)
  {
    //参数重置
   redir_type=NONE_REDIR;
   filename=NULL;
    //输出一个命令行
  _makecommandlineandprint();
  //获取命令字符串
  char usercommand[SIZE];
 int n= _getusercommand(usercommand,sizeof(usercommand));
 if(n<=0)
   return 1;
//检查是否有重定向
_checkredir(usercommand);
//printf("command:%s\n",usercommand);
//printf("redir_type::%d\n",redir_type);
//printf("file:%s\n",filename);

 //命令行字符串分割
 _splitcommand(usercommand,sizeof(usercommand));
//检查命令是否是内建命令
 n=_checkcommand();
 if(n)
   continue;
 
 
 //执行命令
_executecommand();
  }
 return 0;
}
